<template><div><h2 id="redis-6为什么引入多线程" tabindex="-1"><a class="header-anchor" href="#redis-6为什么引入多线程"><span>Redis 6为什么引入多线程</span></a></h2>
<ol>
<li><strong>网络I/O瓶颈</strong></li>
<li><strong>提高带宽利用率</strong></li>
<li><strong>降低延迟</strong></li>
<li><strong>充分利用多核CPU</strong></li>
<li><strong>略微改变的工作模型</strong></li>
</ol>
<h2 id="redis的热key如何解决" tabindex="-1"><a class="header-anchor" href="#redis的热key如何解决"><span>Redis的热Key如何解决</span></a></h2>
<ol>
<li><strong>本地缓存</strong></li>
<li><strong>请求分摊</strong></li>
<li><strong>限流</strong></li>
<li><strong>监控和报警</strong></li>
</ol>
<h2 id="redis的大key如何解决" tabindex="-1"><a class="header-anchor" href="#redis的大key如何解决"><span>Redis的大Key如何解决</span></a></h2>
<p>Redis的大Key问题：单个key所定义的数据量过大，一般单个key超过10kb就会被认为是大Key，这会导致一下问题：</p>
<ol>
<li><strong>网络延迟增大</strong></li>
<li><strong>阻塞Redis性能</strong></li>
<li><strong>内存不足和导致OOM</strong></li>
</ol>
<h4 id="解决方案" tabindex="-1"><a class="header-anchor" href="#解决方案"><span>解决方案：</span></a></h4>
<ol>
<li><strong>分拆大Key</strong></li>
<li><strong>压缩数据</strong></li>
<li><strong>惰性删除</strong></li>
<li><strong>使用SCAN替代KEYS</strong></li>
</ol>
<h2 id="缓存与数据库双写不一致问题如何解决" tabindex="-1"><a class="header-anchor" href="#缓存与数据库双写不一致问题如何解决"><span>缓存与数据库双写不一致问题如何解决</span></a></h2>
<ol>
<li><strong>缓存Aside模式（Cache Aside Pattern）</strong>
<ul>
<li>读操作：先从缓存读取数据，如果缓存未命中（缓存失败），再从数据库读取数据，然后将数据写入缓存。</li>
<li>写操作: 先更新数据库，再删除缓存。这种做法会导致缓存短暂失效，下一次读取会从数据库加载并更新缓存。</li>
</ul>
</li>
<li><strong>更新缓存策略</strong>
<ul>
<li>使用分布式锁：在高并发环境中，在更新操作中加锁，确保同一时间只有一个操作在进行。</li>
</ul>
</li>
<li><strong>用canal中间件异步更新缓存</strong></li>
<li><strong>利用过期时间</strong></li>
</ol>
<h2 id="redis中的key过期了一定会立即删除吗" tabindex="-1"><a class="header-anchor" href="#redis中的key过期了一定会立即删除吗"><span>Redis中的key过期了一定会立即删除吗</span></a></h2>
<p>Redis采用一种<strong>惰性删除（lazy deletion）</strong> 和 <strong>定期删除（perildic expiration）</strong> 相结合的机制来处理过期键。</p>
<h2 id="redis中的key和value的设计原则有哪些" tabindex="-1"><a class="header-anchor" href="#redis中的key和value的设计原则有哪些"><span>Redis中的key和value的设计原则有哪些</span></a></h2>
<h4 id="key设计原则" tabindex="-1"><a class="header-anchor" href="#key设计原则"><span>Key设计原则</span></a></h4>
<ol>
<li><strong>短小精炼</strong></li>
<li><strong>使用命名空间</strong></li>
<li><strong>避免热Key</strong></li>
<li><strong>选择唯一和通用的标识方式</strong></li>
</ol>
<h4 id="value设计原则" tabindex="-1"><a class="header-anchor" href="#value设计原则"><span>Value设计原则</span></a></h4>
<ol>
<li><strong>选择合适的数据结构</strong></li>
<li><strong>限制单个value的大小</strong></li>
<li><strong>利用压缩</strong></li>
<li><strong>TTL的设置</strong></li>
</ol>
<h2 id="redis如何高效安全的遍历所有key" tabindex="-1"><a class="header-anchor" href="#redis如何高效安全的遍历所有key"><span>Redis如何高效安全的遍历所有Key</span></a></h2>
<p><strong>SCAN命令</strong>
<code v-pre>SCAN</code>命令可以避免一次性加载所有Key到内存中（这会耗费大量内存和阻塞服务器），并且它支持增量式迭代。</p>
<h4 id="scan命令的特点" tabindex="-1"><a class="header-anchor" href="#scan命令的特点"><span>SCAN命令的特点</span></a></h4>
<ul>
<li><strong>非阻塞：</strong> 不像<code v-pre>KEYS</code>命令，该命令不会阻塞Redis。</li>
<li><strong>增量式:</strong> <code v-pre>SCAN</code>命令提供了游标，当使用完一个游标后，可以继续用返回的游标继续扫描。</li>
</ul>
<h2 id="redis线上操作最佳实践有那些" tabindex="-1"><a class="header-anchor" href="#redis线上操作最佳实践有那些"><span>Redis线上操作最佳实践有那些</span></a></h2>
<ol>
<li><strong>优化数据结构</strong></li>
<li><strong>设置合理的过期时间</strong></li>
<li><strong>使用持久化策略</strong>
<ul>
<li><strong>结合RDB和AOF</strong>：</li>
<li><strong>定期备份：</strong></li>
</ul>
</li>
<li><strong>监控和警报</strong></li>
<li><strong>规模化和高可用性</strong>
<ul>
<li><strong>使用集群模式：</strong></li>
<li><strong>主动架构和哨兵：</strong></li>
</ul>
</li>
<li><strong>设计良好的命名空间</strong></li>
<li><strong>减少网络延迟</strong></li>
<li><strong>参数调优</strong></li>
<li><strong>客户端重试策略</strong></li>
<li><strong>访问控制与安全</strong></li>
</ol>
<h2 id="一个redis实例最多能存放多少keys" tabindex="-1"><a class="header-anchor" href="#一个redis实例最多能存放多少keys"><span>一个Redis实例最多能存放多少keys</span></a></h2>
<p>Redis的每个实例最多可以存放约2^32-1个keys，即大约42亿个keys。</p>
<h2 id="redis为什么把所有数据都放内存" tabindex="-1"><a class="header-anchor" href="#redis为什么把所有数据都放内存"><span>Redis为什么把所有数据都放内存</span></a></h2>
<ol>
<li><strong>高速读写</strong></li>
<li><strong>简单数据结构</strong></li>
<li><strong>数据持久化</strong></li>
</ol>
<h2 id="什么是缓存击穿、缓存穿透、缓存雪崩" tabindex="-1"><a class="header-anchor" href="#什么是缓存击穿、缓存穿透、缓存雪崩"><span>什么是缓存击穿、缓存穿透、缓存雪崩</span></a></h2>
<h4 id="缓存击穿" tabindex="-1"><a class="header-anchor" href="#缓存击穿"><span>缓存击穿</span></a></h4>
<p>缓存击穿：指的是某个热点数据在缓存中失效，同时大量的请求进来了，一下子打到数据库上。
<strong>解决方案：</strong></p>
<ol>
<li><strong>设置热点数据永不过期：</strong> 对经常被访问的热点数据，设置一个很长的有效期或者永不过期。</li>
<li><strong>互斥锁/同步锁机制:</strong> 在缓存失效后，通过加锁控制对数据库的访问，从而保证只有一个请求能够去查询数据库并更新缓存。</li>
</ol>
<h4 id="缓存穿透" tabindex="-1"><a class="header-anchor" href="#缓存穿透"><span>缓存穿透</span></a></h4>
<p>缓存穿透：指的是用户请求的数据在缓存和数据库中都不存在，从而导致每次请求都落到数据库上。
<strong>解决方案：</strong></p>
<ol>
<li><strong>缓存空对象：</strong> 缓存空对象，但是要设置短的过期时间。</li>
<li><strong>布隆过滤器：</strong> 使用布隆过滤器在访问缓存之前进行一次快速判断，过滤掉不可能存在的数据请求。</li>
</ol>
<h3 id="缓存雪崩" tabindex="-1"><a class="header-anchor" href="#缓存雪崩"><span>缓存雪崩</span></a></h3>
<p>缓存雪崩：指的是在某一时刻，大量的缓存数据共同失效，导致瞬间大量的请求落在数据库上。或者由于分布式缓存节点故障导致缓存失效。
<strong>解决方案：</strong></p>
<ol>
<li><strong>缓存数据过期的时间设置为随机：</strong> 避免大量缓存同一时间过期。</li>
<li><strong>加锁或者队列：</strong> 用锁或队列将请求进行排队。</li>
<li><strong>恢复机制：</strong> 对缓存系统进行降级处理或者配置多级缓存。</li>
</ol>
<h2 id="说说redis的内存淘汰策略" tabindex="-1"><a class="header-anchor" href="#说说redis的内存淘汰策略"><span>说说Redis的内存淘汰策略</span></a></h2>
<ol>
<li><strong>LRU（最近最少使用）：</strong> LRU是Redis默认的内存淘汰策略。</li>
<li><strong>LFU（最不经常用）：</strong>：LFU根据键被访问的频率来判断热度，淘汰访问频率最低的键。</li>
<li><strong>Random（随机淘汰）：</strong> 随机淘汰策略是一种基于概率的淘汰方法，随机选择一个键进行淘汰。</li>
<li><strong>TTL（生成时间）：</strong> TTL策略基于键的过期时间，淘汰剩余生成时间最短的键。</li>
<li><strong>Maxmemory Policy（最大内存策略）：</strong> Redis提供了几种最大内存策略，包括noeviction（禁止淘汰）、allkeys-lru、allkeys-random等。这些策略是在达到设定的最大内存限制后，对写操作返回错误，避免继续写入导致系统崩溃。</li>
</ol>
<h2 id="如何实现redis的高可用" tabindex="-1"><a class="header-anchor" href="#如何实现redis的高可用"><span>如何实现Redis的高可用</span></a></h2>
<ol>
<li><strong>主从复制</strong></li>
<li><strong>哨兵机制</strong></li>
<li><strong>集群模式</strong></li>
</ol>
<h2 id="redis为什么这么快" tabindex="-1"><a class="header-anchor" href="#redis为什么这么快"><span>Redis为什么这么快</span></a></h2>
<ol>
<li><strong>内存存储</strong></li>
<li><strong>单线程模型</strong></li>
<li><strong>高效的数据结构</strong></li>
<li><strong>异步IO</strong></li>
<li><strong>事件驱动架构</strong></li>
<li><strong>优化的操作</strong></li>
</ol>
<h2 id="redis是单线程还是多线程" tabindex="-1"><a class="header-anchor" href="#redis是单线程还是多线程"><span>Redis是单线程还是多线程</span></a></h2>
<p>Redis采用的是<strong>单线程</strong>。通常说的单线程主要指的是Redis对外提供的键值存储服务的主要流程是单线程，即<strong>网络I/O和数据读写是由单个线程来完成的</strong>。这样设计可以避免多线程之间的竞争条件和锁开销，提高了访问共享数据的效率。</p>
<p>然而，除了对外提供的键值存储服务，Redis在某些功能上会使用额外的线程来执行，比如持久化、异步删除和集群数据同步等，这些功能需要在后台执行，不参与主要的网络I/O和数据处理。因此，严格来说，Redis并不是完全单线程。</p>
<h2 id="为什么redis单线程模型效率也能那么高" tabindex="-1"><a class="header-anchor" href="#为什么redis单线程模型效率也能那么高"><span>为什么Redis单线程模型效率也能那么高</span></a></h2>
<ol>
<li><strong>非阻塞IO：</strong> Redis使用了事件驱动的非阻塞IO机制。</li>
<li><strong>内存操作：</strong></li>
<li><strong>单线程避免的开销：</strong></li>
</ol>
<h2 id="说说redis的数据类型" tabindex="-1"><a class="header-anchor" href="#说说redis的数据类型"><span>说说Redis的数据类型</span></a></h2>
<ol>
<li><strong>Stirng</strong></li>
<li><strong>List</strong></li>
<li><strong>Hash</strong></li>
<li><strong>Set</strong></li>
<li><strong>Sorted Set</strong></li>
</ol>
<h2 id="使用redis有哪些好处" tabindex="-1"><a class="header-anchor" href="#使用redis有哪些好处"><span>使用Redis有哪些好处</span></a></h2>
<ol>
<li><strong>缓存功能</strong></li>
<li><strong>高性能</strong></li>
<li><strong>多样化数据类型</strong></li>
<li><strong>支持持久化</strong></li>
<li><strong>支持分布式</strong></li>
<li><strong>简单易用</strong></li>
</ol>
<h2 id="说说对redis操作原子性的理解" tabindex="-1"><a class="header-anchor" href="#说说对redis操作原子性的理解"><span>说说对Redis操作原子性的理解</span></a></h2>
<p>Redis的操作时原子性的，因为Redis的每个命令都是单线程的方式执行，整个命令的执行过程是不可中断的，要么全部执行成功，要么全部执行失败。</p>
<p>主要技术：</p>
<ol>
<li>Redis使用单线程模型，避免了多线程之间的竞争条件和锁开销，从而保证了操作的原子性。</li>
<li>Redis在执行一些复杂的操作时，比如事务、Lua脚本等，会将多个底层操作打包成一个原子性操作，这些底层操作要么全部执行成功，要么全部执行失败。在事务和Lus脚本中，Redis同时支持回滚操作。</li>
</ol>
<h2 id="说说redis的持久化机制" tabindex="-1"><a class="header-anchor" href="#说说redis的持久化机制"><span>说说Redis的持久化机制</span></a></h2>
<h4 id="rdb" tabindex="-1"><a class="header-anchor" href="#rdb"><span>RDB</span></a></h4>
<p>RDB是一种快照持久化的方式，将某个时间点的数据状态以二进制的方式保存到硬盘上。RDB持久化可以通过配置定时或手动触发，也可以设置自动触发的条件。Redis的优点是生成的文件比AOF文件更小，恢复速度也更快，适用于备份和容灾恢复。</p>
<h4 id="aof" tabindex="-1"><a class="header-anchor" href="#aof"><span>AOF</span></a></h4>
<p>AOF是一种追加日志持久化方式，它会将Redis执行的写命令追加到一个文件的末尾，当Redis重启时，他会重新执行这些写命令来恢复数据状态。AOF提供了更可靠的持久化方式，因为它可以保证每个写操作都被记录下来，并且不会发生数据丢失的情况。AOF文件可以根据配置进行同步写入硬盘的频率，包括每秒同步、没写入命令同步和禁用同步三种模式。</p>
<p>在使用持久化机制时，可以选择同时使用RDB和AOF。同时使用两种方式时，Redis再重启会先加载AOF文件来恢复数据，如果AOF文件不存在或损坏，则会尝试加载RDB文件。因此，AOF具有更高的优先级。</p>
<h2 id="说说redis的过期删除策略" tabindex="-1"><a class="header-anchor" href="#说说redis的过期删除策略"><span>说说Redis的过期删除策略</span></a></h2>
<p>Redis的过期策略主要有三种：<strong>惰性删除、定期删除和定期淘汰</strong>。</p>
<ol>
<li><strong>惰性删除：</strong> 惰性删除时Redis默认的过期删除策略。当客户端尝试访问一个已过期的键时，Redis会立即将该键删除，并返回控制。</li>
<li><strong>定期删除：</strong> Redis每隔一段时间执行一次检查，删除那些已过期的键。默认情况下，Redis每秒执行10次检查。</li>
<li><strong>定期淘汰：</strong> 定期淘汰是Redis4.0版本引入的一种新的过期删除策略。与定期删除不同的是，定期淘汰不仅删除已过期的键，而且会主动查询并淘汰一些尚未过期但是由于内存不足而需要释放的键。</li>
</ol>
<h2 id="redis有哪些常见应用场景" tabindex="-1"><a class="header-anchor" href="#redis有哪些常见应用场景"><span>Redis有哪些常见应用场景</span></a></h2>
<ol>
<li><strong>缓存</strong></li>
<li><strong>分布式会话管理</strong></li>
<li><strong>消息队列</strong></li>
<li><strong>实时排行榜/计数器</strong></li>
<li><strong>地理位置信息存储与查询</strong></li>
<li><strong>实时数据分析</strong></li>
</ol>
<h2 id="redis集群的原理是什么" tabindex="-1"><a class="header-anchor" href="#redis集群的原理是什么"><span>Redis集群的原理是什么</span></a></h2>
<p>Redis集群通过数据分片和主从复制实现了横向扩展和高可用。它将数据划分为<strong>16384个哈希槽</strong>，并将这些槽均匀地分配到多个节点上。每个节点复制一部分槽的数据，实现了数据的分散存储和负载均衡。</p>
<h2 id="redis集群方案什么时候会导致整个集群不可用" tabindex="-1"><a class="header-anchor" href="#redis集群方案什么时候会导致整个集群不可用"><span>Redis集群方案什么时候会导致整个集群不可用</span></a></h2>
<ol>
<li><strong>多个主节点同时故障</strong></li>
<li><strong>集群管理节点故障</strong></li>
<li><strong>网络分区</strong></li>
<li><strong>配置错误</strong></li>
<li><strong>内存不足</strong></li>
</ol>
<h2 id="redis集群会有写操作丢失吗-为什么" tabindex="-1"><a class="header-anchor" href="#redis集群会有写操作丢失吗-为什么"><span>Redis集群会有写操作丢失吗？为什么</span></a></h2>
<p>在Redis集群中，由于采用了主从复制模型的<strong>异步复制机制</strong>，写操作有一定的丢失风险。</p>
<p>当客户端向主节点大宋写操作时，主节点会<strong>立即返回成功响应</strong>，而不等待所有从节点执行复制。如果主节点在执行完写操作后出现故障或网路问题，导致从节点无法及时接收到复制操作，那么这些未复制的写操作将会丢失。</p>
<p><strong>解决方案：</strong></p>
<ol>
<li>定期监测集群状态，确保主从节点之间的复制正常进行。</li>
<li>设置合理的持久化策略，将数据写入磁盘或使用AOF模式以便数据恢复。</li>
<li>在应用程序层面实施数据确认机制，检查写操作是否成功。</li>
</ol>
<h2 id="说说你对pipeline的理解" tabindex="-1"><a class="header-anchor" href="#说说你对pipeline的理解"><span>说说你对pipeline的理解</span></a></h2>
<p><code v-pre>PIPELINE</code>是Redis提供额度优化命令的执行机制，通过减少网络往返次数和批量处理命令来提高性能。
它将多个命令打包发送给服务器，减少了网络延迟，提升了吞吐量。
使用PIPELINE还可以降低服务器资源消耗，提高整体效率。适用于需要连续执行多个命令或批量操作的场景，特别适合延迟敏感和大量请求的应用程序。</p>
<h2 id="什么是redis哨兵机制" tabindex="-1"><a class="header-anchor" href="#什么是redis哨兵机制"><span>什么是Redis哨兵机制</span></a></h2>
<p>Redis哨兵是一种用于高可用的解决方案，它可以监控Redis主从复制模式下的主节点和从节点，发现节点故障，并自动进行故障转移，保证Redis系统的稳定性和可靠性。
Redis哨兵机制由多个相互独立的进程组成，这些进程使用TCP/IP协议相互通信，实现Redis节点的监控和故障转移。哨兵机制的关键进行包括：</p>
<ol>
<li><strong>sentinel：</strong> 主进程，用于监控Redis节点的状态，并执行故障转移操作。</li>
<li><strong>minitor：</strong> 哨兵进程，用于监控Redis的主节点和从节点是否正常工作，并在需要时通知其他哨兵进程和客户端。</li>
<li><strong>judge：</strong> 哨兵进程，用于对节点的健康状况进行评估，并根据预定义的阈值决定是否要将一个不健康的节点标记为&quot;主观下线&quot;。</li>
<li><strong>failover：</strong> 哨兵进程，负责执行故障转移操作，将主节点故障时选举出来的从节点晋升为新的主节点，并通知其他Redis节点更新配置信息。</li>
</ol>
<h2 id="redis到底支不支持事务" tabindex="-1"><a class="header-anchor" href="#redis到底支不支持事务"><span>Redis到底支不支持事务？</span></a></h2>
<p><strong>Redos是支持事务的，但是它与传统的关系型数据库中的事务是有所不同的。</strong></p>
<p>概念：可以一次执行多个命令，本质是一组命令的集合。一个事务中的所有命令都会被序列化，按顺序地串行化执行而不会被其他命令插入。
<strong>常用命令：</strong></p>
<ul>
<li><strong>multi：</strong> 开启一个事务，multi执行之后，客户端可以继续向服务器发送任意多条命令，这些命令不会被立即执行，而是被放到一个队列中。</li>
<li><strong>exec：</strong> 执行队列中的所有命令。</li>
<li><strong>discard：</strong> 中断当前事务，然后清空事务队列并放弃执行事务。</li>
<li><strong>watch key1 key2 ... :</strong> 监视一个（或多个）key，如果在事务执行之前这个（或这些）key被其他命令所改动，那么事务将被打断。</li>
</ul>
<p>Redis的watch命令是一种乐观锁的实现方式。</p>
<p>事务三阶段：</p>
<ol>
<li>开启：multi</li>
<li>入队</li>
<li>执行： exec</li>
</ol>
</div></template>


